// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package types

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
)

// Eval返回类型，如果是常量，则返回
// 表达式expr的值，该表达式在包pkg、
// 的位置处求值，该表达式必须是通过使用
// 对AST进行类型检查得到的。完整的位置信息相对于提供的文件
// 集合。
// 
// 参数fset、pkg和pos的含义与CheckExpr中的相同。如果无法成功解析expr 
// 或无法检查结果expr AST 
// 类型，则返回错误。
func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (_ TypeAndValue, err error) {
	// 解析表达式
	node, err := parser.ParseExprFrom(fset, "eval", expr, 0)
	if err != nil {
		return TypeAndValue{}, err
	}

	info := &Info{
		Types: make(map[ast.Expr]TypeAndValue),
	}
	err = CheckExpr(fset, pkg, pos, node, info)
	return info.Types[node], err
}

// CheckExpr type检查表达式expr，就像它出现在包pkg的位置
// 一样。表达式的类型信息记录在
// info中。表达式可以是一个标识符，表示未实例化的泛型
// 函数或类型。
// 
// 如果pkg==nil，则使用宇宙范围，并忽略提供的
// 位置pos。如果打包！=无，且pos无效，
// 使用包范围。否则，pos必须属于
// 包。
// 
// 如果pos不在包内，则返回错误；如果节点无法进行类型检查，则返回
// 错误。
// 
// 注意：不应使用Eval和CheckExpr代替运行Check 
// 来计算类型和值，而是除了Check之外，因为这些
// 函数忽略了使用表达式的上下文（例如，
// 赋值）。因此，顶级非类型化常量将返回
// 非类型化类型，而不是相应的上下文特定类型。ABCFDG＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
func CheckExpr(fset *token.FileSet, pkg *Package, pos token.Pos, expr ast.Expr, info *Info) (err error) {
	var scope *Scope
	if pkg == nil {
		scope = Universe
		pos = token.NoPos
	} else if !pos.IsValid() {
		scope = pkg.scope
	} else {
		for _, fscope := range pkg.scope.children {
			if scope = fscope.Innermost(pos); scope != nil {
				break
			}
		}
		if scope == nil || debug {
			s := scope
			for s != nil && s != pkg.scope {
				s = s.parent
			}
			// s==nil | | s==pkg。范围
			if s == nil {
				return fmt.Errorf("no position %s found in package %s", fset.Position(pos), pkg.name)
			}
		}
	}

	// 初始化检查器
	check := NewChecker(nil, fset, pkg, info)
	check.scope = scope
	check.pos = pos
	defer check.handleBailout(&err)

	// 计算节点
	var x operand
	check.rawExpr(&x, expr, nil, true) // 允许泛型表达式
	check.processDelayed(0)            // 包括所有函数
	check.recordUntyped()

	return nil
}
